package org.app4j.passport.service;

import java.util.Date;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.servlet.http.Cookie;

import org.app4j.annotation.ExceptionHandler;
import org.app4j.annotation.Get;
import org.app4j.annotation.Post;
import org.app4j.annotation.Service;
import org.app4j.passport.dao.UserDao;
import org.app4j.passport.domain.User;
import org.app4j.passport.service.exception.AuthException;
import org.app4j.passport.service.exception.DuplicateUserNameException;
import org.app4j.runtime.Event;
import org.app4j.runtime.ManagedService;
import org.app4j.runtime.util.HashBuilder;
import org.app4j.runtime.util.HashBuilder.Hash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Chi
 */
@Service
public class UserService extends ManagedService {
  private final Logger logger = LoggerFactory.getLogger(UserService.class);
  @Inject
  UserDao userDao;

  private HashBuilder hashBuilder;

  private final static String USER_KEY = "current_user";

  @PostConstruct
  public void start() {
    String key = this.getProperty("token.salt", "12345678");
    this.hashBuilder = new HashBuilder(key, Hash.SHA1);
  }

  @Post("login")
  public User login(String username, String password, Event event) {
    User user = this.findUserByName(username);
    if (user == null) {
      return null;
    }
    if (!user.getPassword().equals(hashBuilder.hash(password))) {
      event.error(new AuthException(this.getMessage("用户名或密码有误.", event.getLocale())));
      return null;
    }
    user.setPassword(null);
    saveCurrentUserToCookie(user);
    Event.currentEvent().forward("");
    return user;
  }

  @Get("tologin")
  public void toLogin() {
    Event.currentEvent().redirect("passport/view.login.jsp");
  }


  private void saveCurrentUserToCookie(User user) {
    Cookie cookie = new Cookie(USER_KEY, getToken(user));
    cookie.setMaxAge(30 * 60 * 1000);
    cookie.setPath("/passport");
    Event.currentEvent().getResponse().addCookie(cookie);
  }

  @Post("register")
  public User register(String username, String password, String email, String roles) {
    if (this.findUserByName(username) != null) {
      throw new DuplicateUserNameException();
    }
    User user = new User();
    user.setUsername(username);
    user.setPassword(password);
    user.setEmail(email);
    user.setRoles(roles);
    user.setCreateTime(new Date());
    user.setLastUpdateTime(new Date());
    user.setStatus(ObjectStatus.NORMAL.value());
    this.saveUser(user);

    saveCurrentUserToCookie(user);

    return user;
  }

  @Get("logout")
  public User loginout(String token, Event event) {
    UserTokenParser parser = new UserTokenParser();
    User user = parser.fromToken(token);
    if (user == null) {
      return null;
    }
    removeCurrentCookie();
    event.forward("");// TODO:
    return user;
  }

  private void removeCurrentCookie() {
    Cookie[] cookies = Event.currentEvent().getRequest().getCookies();
    for (Cookie cookie : cookies) {
      if (USER_KEY.equals(cookie.getName())) {
        cookie.setMaxAge(0);
        cookie.setValue(null);
        break;
      }
    }
  }

  public String getToken(User user) {
    StringBuilder b = new StringBuilder();
    b.append(user.getId());
    b.append("|");
    b.append(user.getUsername());
    b.append("|");

    return this.hashBuilder.hash(b.toString());
  }

  public User getUser(String token) {
    return null;
  }

  public boolean isTokenMataches(Long userId, Long username, String token) {
    StringBuilder b = new StringBuilder();
    b.append(userId);
    b.append("|");
    b.append(username);
    b.append("|");

    return this.hashBuilder.hash(b.toString()).equals(token);
  }


  public void saveUser(User user) {
    this.userDao.save(user);
  }


  public User findUserByName(String username) {
    return this.userDao.findByUsername(username);
  }

  public User findUserById(Long userId) {
    User user = new User();
    user.setId(-1L);
    user.setUsername("Tester");
    return user;
  }

  @ExceptionHandler(RuntimeException.class)
  public Throwable onException(Throwable t) {
    t.printStackTrace();
    this.logger.error(Event.currentEvent().getActionMapping() + " failed, " + t.getMessage());
    return t;
  }


}
